/*
 ** client.c -- a stream socket client demo
 */

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <map>
#include <string>
#include <fstream>
#include <iostream>
#include <utility>

#include <arpa/inet.h>
#include <fstream>
using namespace std;

#define PORT "3490" // the port client will be connecting to 

#define MAXDATASIZE 1025 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:

void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET)
    {
        return &(((struct sockaddr_in*) sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*) sa)->sin6_addr);
}

int sendall(int s, char *buf, int *len)
{
    int total = 0; // how many bytes we've sent
    int bytesleft = *len; // how many we have left to send
    int n;

    while (total < *len)
    {
        n = send(s, buf + total, bytesleft, 0);
        if (n == -1)
        {
            break;
        }
        total += n;
        bytesleft -= n;
    }

    *len = total; // return number actually sent here

    return n == -1 ? -1 : 0; // return -1 on failure, 0 on success
}

void tcp_put(int socket)
{
    int size = 4096;
    char command[80], temp[100];
    send(socket, "put", 1024, 0);
    printf("Enter file name:");
    gets(command);
    
    char buffer[4096];
    // get file sizr
    struct stat st;
    int status = 0,count = 0;

    

    FILE *f;
    f = fopen(command, "r");

    if (f == NULL)
    {
        printf("Bad file name!");
    }
    else
    {
         send(socket, command, 1024, 0);
        while (fgets(temp, sizeof(temp), f))
        {
            if(count <= 40)
            {
            strcat(buffer,temp);
            count++;
            }
            count++;
        }
       
       
        send(socket, buffer, 4096, 0);

    }
}

void tcp_get(int socket)
{
    int numbytes;
    char buf[4096], command[] = "get";
    char fileName[1025];
    send(socket, command, 1024, 0);
    puts("Enter file name to download:");
    gets(fileName);
    send(socket, fileName, 1024, 0);
    if ((numbytes = recv(socket, buf, 4095, 0)) == -1)
    {
        perror("recv");
        //exit(1);
    }

    buf[numbytes] = '\0';

    ofstream f;
    f.open(fileName);
    f << buf;
    f.close();

}

void tcp_ls(int socket)
{
    int numbytes;
    char buf[MAXDATASIZE], command[] = "ls";
    char fileName[1024];
    send(socket, command, 1024, 0);
    //gets(fileName);
    //send(socket, fileName, strlen(fileName), 0);
    if ((numbytes = recv(socket, buf, MAXDATASIZE - 1, 0)) == -1)
    {
        perror("recv");
        //exit(1);
    }

    buf[numbytes] = '\0';

    printf("Directory listing: \n '%s'\n", buf);
}

void tcp_cd(int socket)
{
    int numbytes;
    char buf[MAXDATASIZE], command[] = "cd";
    char fileName[1024];
    send(socket, command, 1024, 0);
    printf("Enter new directory: \n");
    gets(fileName);
    send(socket, fileName, strlen(fileName), 0);
    if ((numbytes = recv(socket, buf, MAXDATASIZE - 1, 0)) == -1)
    {
        perror("recv");
        //exit(1);
    }

    buf[numbytes] = '\0';

    printf("Status: \n '%s'\n", buf);

}

void tcp_lcd(int socket)
{
    char fileName[256];
    printf("Enter new local directory: \n");
    gets(fileName);
    chdir(fileName);
    printf("local directory changed!");
}

void tcp_cwd(int socket)
{
    int numbytes;
    char buf[MAXDATASIZE];

    send(socket, "cwd", MAXDATASIZE - 1, 0);
    if ((numbytes = recv(socket, buf, MAXDATASIZE - 1, 0)) == -1)
    {
        perror("recv");
        //exit(1);
    }
    buf[numbytes] = '\0';

    printf("Directory is: \n '%s' \n", buf);
}

int main(int argc, char *argv[])
{
    typedef void(*StringFunc)(int socket);
    std::map<std::string, StringFunc> commamdToTcpFuncMap;

    commamdToTcpFuncMap.insert(std::make_pair("cwd", &tcp_cwd));
    commamdToTcpFuncMap.insert(std::make_pair("put", &tcp_put));
    commamdToTcpFuncMap.insert(std::make_pair("get", &tcp_get));
    commamdToTcpFuncMap.insert(std::make_pair("ls", &tcp_ls));
    commamdToTcpFuncMap.insert(std::make_pair("cd", &tcp_cd));
    commamdToTcpFuncMap.insert(std::make_pair("lcd", &tcp_lcd));


    int sockfd, numbytes, msgReturnStatus;
    char buf[MAXDATASIZE];
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];
    char command[10];
    char exit_command[] = "exit";

    if (argc != 2)
    {
        fprintf(stderr, "usage: client hostname\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0)
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and connect to the first we can
    for (p = servinfo; p != NULL; p = p->ai_next)
    {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                             p->ai_protocol)) == -1)
        {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
        {
            close(sockfd);
            perror("client: connect");
            continue;
        }

        break;
    }

    if (p == NULL)
    {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *) p->ai_addr),
              s, sizeof s);
    printf("client: connecting to %s\n", s);

    freeaddrinfo(servinfo); // all done with this structure

    if ((numbytes = recv(sockfd, buf, MAXDATASIZE - 1, 0)) == -1)
    {
        perror("recv");
        exit(1);
    }

    buf[numbytes] = '\0';

    printf("client: received '%s'\n", buf);

    // Get first command
    gets(command);

    while (strcmp(command, exit_command) != 0)
    {
        if (commamdToTcpFuncMap.find(command) != commamdToTcpFuncMap.end())
        {
            // run command
            commamdToTcpFuncMap[command](sockfd);
        }
        else
        {
            printf("wrong command \n");
        }

        // Get next command
        printf("Enter next command: \n");
        // memset(command,0,sizeof(command));
        command[2] = '\0';
        gets(command);
    }

    close(sockfd);

    return 0;
}
